package com.zyf.chapter02.behavior.observer.eventbus;

import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;

/**
 * @author Observer 注册表
 */
public class ObserverRegistry {
    /**
     * key 为 eventType
     */
    private ConcurrentHashMap<Class<?>, CopyOnWriteArraySet<ObserverAction>> registry =
            new ConcurrentHashMap<>();

    public void register(Object observer) {
        Map<Class<?>, Collection<ObserverAction>> observerAction = findAllObserverAction(observer);
        // 遍历注册表（即遍历 eventType）
        for (Map.Entry<Class<?>, Collection<ObserverAction>> entry : observerAction.entrySet()) {
            Class<?> eventType = entry.getKey();
            Collection<ObserverAction> eventActions = entry.getValue();
            // 之前是否注册过相应的 eventType，未注册则注册
            CopyOnWriteArraySet<ObserverAction> registeredEventActions = registry.get(eventType);
            if (registeredEventActions == null) {
                // 未注册则 put
                registry.putIfAbsent(eventType, new CopyOnWriteArraySet<>());
                registeredEventActions = registry.get(eventType);
            }
            registeredEventActions.addAll(eventActions);
        }
    }

    /**
     * 判断是否存在指定的 eventType，eventType 为 {@param event} 的 class 类型
     * @param event 事件
     * @return eventType 相关的 ObserverAction
     */
    public List<ObserverAction> getMatchedObserverActions(Object event) {
        ArrayList<ObserverAction> matchedObservers = new ArrayList<>();
        Class<?> postedEventType = event.getClass();
        for (Map.Entry<Class<?>, CopyOnWriteArraySet<ObserverAction>> entry : registry.entrySet()) {
            Class<?> eventType = entry.getKey();
            Collection<ObserverAction> eventActions = entry.getValue();
            // postedEventType 是否是 eventType 的类型/超类/接口
            if (postedEventType.isAssignableFrom(eventType)) {
                matchedObservers.addAll(eventActions);
            }
        }
        return matchedObservers;
    }

    /**
     * 搜索并返回与 eventType 相关的的所有 ObserverAction
     * @param observer 观察者
     * @return key 为事件（event）类型
     */
    private Map<Class<?>, Collection<ObserverAction>> findAllObserverAction(Object observer) {
        HashMap<Class<?>, Collection<ObserverAction>> observerActions = new HashMap<>();
        Class<?> clazz = observer.getClass();
        for (Method method : getAnnotatedMethods(clazz)) {
            // 取出方法中的参数列表
            Class<?>[] parameterTypes = method.getParameterTypes();
            // 获取第一个参数类型，即 event 类型
            Class<?> eventType = parameterTypes[0];
            // 观察者集合中是否存在该类型，如果不存在，则 put
            if (!observerActions.containsKey(eventType)) {
                observerActions.put(eventType, new ArrayList<>());
            }
            // 指定 event类型对应的 ObserverAction
            observerActions.get(eventType).add(new ObserverAction(observer, method));
        }
        return observerActions;
    }

    /**
     * 判断对象中是否存在使用了 {@link Subscribe} 注解的方法
     * @param clazz 指定的 class
     * @return 添加了 {@link Subscribe} 注解的方法
     */
    private List<Method> getAnnotatedMethods(Class<?> clazz) {
        ArrayList<Method> annotatedMethods = new ArrayList<>();
        for (Method method : clazz.getDeclaredMethods()) {
            if (method.isAnnotationPresent(Subscribe.class)) {
                Class<?>[] parameterTypes = method.getParameterTypes();
                Preconditions.checkArgument(parameterTypes.length == 1,
                        "Method %s has @Subscribe annotation bug has %s parameters."
                            + "Subscribe methods must have exactly 1 parameter.",
                        method, parameterTypes.length);
                annotatedMethods.add(method);
            }
        }
        return annotatedMethods;
    }
}
